From 3dd188fe7e0243981db0c4b731e918f209d9c7c4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Sun, 8 Jul 2018 20:08:15 +0200 Subject: [PATCH] gl renderer: Cache blurred outset shadow nodes Since these are particularly expensive to render and we have a pretty big one used in every client-side decorated window. --- gsk/gl/gskglrenderer.c | 148 +++++++++++++++++-------------- gsk/gl/gskglshadowcache.c | 121 +++++++++++++++++++++++++ gsk/gl/gskglshadowcacheprivate.h | 31 +++++++ gsk/meson.build | 3 +- 4 files changed, 237 insertions(+), 66 deletions(-) create mode 100644 gsk/gl/gskglshadowcache.c create mode 100644 gsk/gl/gskglshadowcacheprivate.h diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index ad2e8c5cf7..76a54728de 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -13,6 +13,7 @@ #include "gskglglyphcacheprivate.h" #include "gskglrenderopsprivate.h" #include "gskcairoblurprivate.h" +#include "gskglshadowcacheprivate.h" #include "gskprivate.h" @@ -53,7 +54,6 @@ glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\ }G_STMT_END - static void G_GNUC_UNUSED print_render_node_tree (GskRenderNode *root, int level) { @@ -266,6 +266,7 @@ struct _GskGLRenderer GArray *render_ops; GskGLGlyphCache glyph_cache; + GskGLShadowCache shadow_cache; #ifdef G_ENABLE_DEBUG struct { @@ -1077,6 +1078,7 @@ render_outset_shadow_node (GskGLRenderer *self, int prev_render_target; int texture_id, render_target; int blurred_texture_id, blurred_render_target; + int cached_tid; /* offset_outline is the minimal outline we need to draw the given drop shadow, * enlarged by the spread and offset by the blur radius. */ @@ -1098,77 +1100,92 @@ render_outset_shadow_node (GskGLRenderer *self, texture_width = offset_outline.bounds.size.width + blur_extra; texture_height = offset_outline.bounds.size.height + blur_extra; - texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height); - gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id); - gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id); - render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE); - - - graphene_matrix_init_ortho (&item_proj, - 0, texture_width, 0, texture_height, - ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); - graphene_matrix_scale (&item_proj, 1, -1, 1); - graphene_matrix_init_identity (&identity); + cached_tid = gsk_gl_shadow_cache_get_texture_id (&self->shadow_cache, + self->gl_driver, + &offset_outline, + blur_radius); + if (cached_tid == 0) + { + texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height); + gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id); + gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id); + render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE); + + + graphene_matrix_init_ortho (&item_proj, + 0, texture_width, 0, texture_height, + ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); + graphene_matrix_scale (&item_proj, 1, -1, 1); + graphene_matrix_init_identity (&identity); + + prev_render_target = ops_set_render_target (builder, render_target); + op.op = OP_CLEAR; + ops_add (builder, &op); + prev_projection = ops_set_projection (builder, &item_proj); + prev_modelview = ops_set_modelview (builder, &identity); + prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); + + /* Draw outline */ + ops_set_program (builder, &self->color_program); + prev_clip = ops_set_clip (builder, &offset_outline); + ops_set_color (builder, gsk_outset_shadow_node_peek_color (node)); + ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { + { { 0, }, { 0, 1 }, }, + { { 0, texture_height }, { 0, 0 }, }, + { { texture_width, }, { 1, 1 }, }, - prev_render_target = ops_set_render_target (builder, render_target); - op.op = OP_CLEAR; - ops_add (builder, &op); - prev_projection = ops_set_projection (builder, &item_proj); - prev_modelview = ops_set_modelview (builder, &identity); - prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height)); + { { texture_width, texture_height }, { 1, 0 }, }, + { { 0, texture_height }, { 0, 0 }, }, + { { texture_width, }, { 1, 1 }, }, + }); - /* Draw outline */ - ops_set_program (builder, &self->color_program); - prev_clip = ops_set_clip (builder, &offset_outline); - ops_set_color (builder, gsk_outset_shadow_node_peek_color (node)); - ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { - { { 0, }, { 0, 1 }, }, - { { 0, texture_height }, { 0, 0 }, }, - { { texture_width, }, { 1, 1 }, }, - - { { texture_width, texture_height }, { 1, 0 }, }, - { { 0, texture_height }, { 0, 0 }, }, - { { texture_width, }, { 1, 1 }, }, - }); - - blurred_texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height); - gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id); - gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id); - blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE); - - ops_set_render_target (builder, blurred_render_target); - op.op = OP_CLEAR; - ops_add (builder, &op); + blurred_texture_id = gsk_gl_driver_create_permanent_texture (self->gl_driver, texture_width, texture_height); + gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id); + gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id); + blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE); - gsk_rounded_rect_init_from_rect (&blit_clip, - &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height), 0.0f); + ops_set_render_target (builder, blurred_render_target); + op.op = OP_CLEAR; + ops_add (builder, &op); - ops_set_program (builder, &self->blur_program); - op.op = OP_CHANGE_BLUR; - op.blur.size.width = texture_width; - op.blur.size.height = texture_height; - op.blur.radius = blur_radius; - ops_add (builder, &op); + gsk_rounded_rect_init_from_rect (&blit_clip, + &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height), 0.0f); - ops_set_clip (builder, &blit_clip); - ops_set_texture (builder, texture_id); - ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { - { { 0, 0 }, { 0, 1 }, }, - { { 0, texture_height }, { 0, 0 }, }, - { { texture_width, 0 }, { 1, 1 }, }, + ops_set_program (builder, &self->blur_program); + op.op = OP_CHANGE_BLUR; + op.blur.size.width = texture_width; + op.blur.size.height = texture_height; + op.blur.radius = blur_radius; + ops_add (builder, &op); - { { texture_width, texture_height }, { 1, 0 }, }, - { { 0, texture_height }, { 0, 0 }, }, - { { texture_width, 0 }, { 1, 1 }, }, - }); + ops_set_clip (builder, &blit_clip); + ops_set_texture (builder, texture_id); + ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) { + { { 0, 0 }, { 0, 1 }, }, + { { 0, texture_height }, { 0, 0 }, }, + { { texture_width, 0 }, { 1, 1 }, }, + { { texture_width, texture_height }, { 1, 0 }, }, + { { 0, texture_height }, { 0, 0 }, }, + { { texture_width, 0 }, { 1, 1 }, }, + }); - ops_set_clip (builder, &prev_clip); - ops_set_viewport (builder, &prev_viewport); - ops_set_modelview (builder, &prev_modelview); - ops_set_projection (builder, &prev_projection); - ops_set_render_target (builder, prev_render_target); + ops_set_clip (builder, &prev_clip); + ops_set_viewport (builder, &prev_viewport); + ops_set_modelview (builder, &prev_modelview); + ops_set_projection (builder, &prev_projection); + ops_set_render_target (builder, prev_render_target); + + gsk_gl_shadow_cache_commit (&self->shadow_cache, + &offset_outline, + blur_radius, + blurred_texture_id); + } + else + { + blurred_texture_id = cached_tid; + } ops_set_program (builder, &self->outset_shadow_program); ops_set_texture (builder, blurred_texture_id); @@ -1393,8 +1410,6 @@ render_outset_shadow_node (GskGLRenderer *self, } } - - ops_set_clip (builder, &prev_clip); } static inline void @@ -2006,6 +2021,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer, return FALSE; gsk_gl_glyph_cache_init (&self->glyph_cache, renderer, self->gl_driver); + gsk_gl_shadow_cache_init (&self->shadow_cache); return TRUE; } @@ -2030,6 +2046,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer) glDeleteProgram (self->programs[i].id); gsk_gl_glyph_cache_free (&self->glyph_cache); + gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver); g_clear_object (&self->gl_profiler); g_clear_object (&self->gl_driver); @@ -2524,6 +2541,7 @@ gsk_gl_renderer_do_render (GskRenderer *renderer, gsk_gl_driver_begin_frame (self->gl_driver); gsk_gl_glyph_cache_begin_frame (&self->glyph_cache); + gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver); memset (&render_op_builder, 0, sizeof (render_op_builder)); render_op_builder.renderer = self; diff --git a/gsk/gl/gskglshadowcache.c b/gsk/gl/gskglshadowcache.c new file mode 100644 index 0000000000..8d8960e88b --- /dev/null +++ b/gsk/gl/gskglshadowcache.c @@ -0,0 +1,121 @@ + +#include "gskglshadowcacheprivate.h" + +typedef struct +{ + GskRoundedRect outline; + float blur_radius; +} CacheKey; + +typedef struct +{ + int texture_id; + guint used : 1; +} CacheValue; + +static gboolean +key_equal (const void *x, + const void *y) +{ + const CacheKey *a = x; + const CacheKey *b = y; + + return memcmp (&a->outline, &b->outline, sizeof (GskRoundedRect)) == 0 && + a->blur_radius == b->blur_radius; +} + +void +gsk_gl_shadow_cache_init (GskGLShadowCache *self) +{ + self->textures = g_hash_table_new_full (g_direct_hash, key_equal, g_free, g_free); +} + +void +gsk_gl_shadow_cache_free (GskGLShadowCache *self, + GskGLDriver *gl_driver) +{ + g_hash_table_unref (self->textures); + self->textures = NULL; +} + +void +gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self, + GskGLDriver *gl_driver) +{ + GHashTableIter iter; + CacheKey *key; + CacheValue *value; + + /* We remove all textures with used = FALSE since those have not been used in the + * last frame. For all others, we reset the `used` value to FALSE instead and see + * if they end up with TRUE in the next call to begin_frame. */ + + g_hash_table_iter_init (&iter, self->textures); + while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value)) + { + if (!value->used) + { + /* Remove */ + gsk_gl_driver_destroy_texture (gl_driver, value->texture_id); + g_hash_table_iter_remove (&iter); + } + else + { + value->used = FALSE; + } + } +} + +/* XXX + * The offset origin should always be at 0/0, or the blur radius should just go + * away since it defines the origin position anyway? + */ +int +gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self, + GskGLDriver *gl_driver, + const GskRoundedRect *shadow_rect, + float blur_radius) +{ + CacheValue *value; + + g_assert (self != NULL); + g_assert (gl_driver != NULL); + g_assert (shadow_rect != NULL); + + value = g_hash_table_lookup (self->textures, + &(CacheKey){ + *shadow_rect, + blur_radius + }); + + if (value == NULL) + return 0; + + value->used = TRUE; + + return value->texture_id; +} + +void +gsk_gl_shadow_cache_commit (GskGLShadowCache *self, + const GskRoundedRect *shadow_rect, + float blur_radius, + int texture_id) +{ + CacheKey *key; + CacheValue *value; + + g_assert (self != NULL); + g_assert (shadow_rect != NULL); + g_assert (texture_id > 0); + + key = g_new0 (CacheKey, 1); + key->outline = *shadow_rect; + key->blur_radius = blur_radius; + + value = g_new0 (CacheValue, 1); + value->used = TRUE; + value->texture_id = texture_id; + + g_hash_table_insert (self->textures, key, value); +} diff --git a/gsk/gl/gskglshadowcacheprivate.h b/gsk/gl/gskglshadowcacheprivate.h new file mode 100644 index 0000000000..e4f4814f21 --- /dev/null +++ b/gsk/gl/gskglshadowcacheprivate.h @@ -0,0 +1,31 @@ + + +#ifndef __GSK_GL_SHADOW_CACHE_H__ +#define __GSK_GL_SHADOW_CACHE_H__ + +#include +#include "gskgldriverprivate.h" +#include "gskroundedrect.h" + +typedef struct +{ + GHashTable *textures; +} GskGLShadowCache; + + +void gsk_gl_shadow_cache_init (GskGLShadowCache *self); +void gsk_gl_shadow_cache_free (GskGLShadowCache *self, + GskGLDriver *gl_driver); +void gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self, + GskGLDriver *gl_driver); +int gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self, + GskGLDriver *gl_driver, + const GskRoundedRect *shadow_rect, + float blur_radius); +void gsk_gl_shadow_cache_commit (GskGLShadowCache *self, + const GskRoundedRect *shadow_rect, + float blur_radius, + int texture_id); + + +#endif diff --git a/gsk/meson.build b/gsk/meson.build index 2b9115bb2e..8ad27f7de6 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -41,7 +41,8 @@ gsk_private_sources = files([ 'gl/gskglglyphcache.c', 'gl/gskglimage.c', 'gl/gskgldriver.c', - 'gl/gskglrenderops.c' + 'gl/gskglrenderops.c', + 'gl/gskglshadowcache.c', ]) gsk_public_headers = files([ -- 2.30.2